package com.teamfractal.fracdustry.common.block.impl.connectivities;

import com.google.common.collect.Maps;
import com.teamfractal.fracdustry.common.block.init.FDBlocks;
import com.teamfractal.fracdustry.common.blockentity.Connectivities.FDCableBlockEntity;
import com.teamfractal.fracdustry.common.util.energynetwork.EnergyNetwork;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.material.Material;
import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.fml.common.Mod;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @author Tapio, ustc-zzzz
 */
@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD)
public class FDCableBlock extends Block implements EntityBlock {
    public static final Map<Direction, BooleanProperty> PROPERTY_MAP;

    static
    {
        Map<Direction, BooleanProperty> map = Maps.newEnumMap(Direction.class);
        map.put(Direction.NORTH, BlockStateProperties.NORTH);
        map.put(Direction.EAST, BlockStateProperties.EAST);
        map.put(Direction.SOUTH, BlockStateProperties.SOUTH);
        map.put(Direction.WEST, BlockStateProperties.WEST);
        map.put(Direction.UP, BlockStateProperties.UP);
        map.put(Direction.DOWN, BlockStateProperties.DOWN);
        PROPERTY_MAP = Collections.unmodifiableMap(map);
    }

    public FDCableBlock()
    {
        super(Block.Properties.of(Material.WOOL).strength(2).explosionResistance(2).sound(SoundType.WOOL));
    }

    @Override
    protected void createBlockStateDefinition(@Nonnull StateDefinition.Builder<Block, BlockState> builder)
    {
        //Sure?
        builder.add(PROPERTY_MAP.values().toArray(new BooleanProperty[0]));
        super.createBlockStateDefinition(builder);
    }


    @Nonnull
    @Override
    @SuppressWarnings("deprecation")
    public VoxelShape getShape(@Nonnull BlockState state, @Nonnull BlockGetter world, @Nonnull BlockPos pos, @Nonnull CollisionContext context)
    {
        return Block.box(4, 4, 4, 12, 12, 12);
    }

    @Nonnull
    @Override
    @SuppressWarnings("deprecation")
    public VoxelShape getCollisionShape(@Nonnull BlockState state, @Nonnull BlockGetter world, @Nonnull BlockPos pos, @Nonnull CollisionContext context)
    {
        return Shapes.empty();
    }

    //BlockItem's tooltip
    @Override
    public void appendHoverText(@Nonnull ItemStack stack, @Nullable BlockGetter reader, List<Component> list, @Nonnull TooltipFlag flags) {
        list.add(new TranslatableComponent("tooltips.fracdustry.energy_cable").withStyle(ChatFormatting.DARK_GRAY));
    }

    @Override
    public BlockState getStateForPlacement(@Nonnull BlockPlaceContext context) {
        BlockState state = this.defaultBlockState();
        for (Direction facing : Direction.values())
        {
            Level world = context.getLevel();
            BlockPos facingPos = context.getClickedPos().offset(facing.getNormal());
            BlockState facingState = world.getBlockState(facingPos);
            state = state.setValue(PROPERTY_MAP.get(facing), this.canConnect(world, facing.getOpposite(), facingPos, facingState));
        }
        return state;
    }

    @Nonnull
    @Override
    @SuppressWarnings("deprecation")
    public BlockState updateShape(@Nonnull BlockState state, @Nonnull Direction facing, @Nonnull BlockState facingState, @Nonnull LevelAccessor world, @Nonnull BlockPos pos, @Nonnull BlockPos facingPos)
    {
        //Sure?
        return state.setValue(PROPERTY_MAP.get(facing), this.canConnect(world, facing.getOpposite(), facingPos, facingState));
    }

    private boolean canConnect(@Nonnull LevelAccessor world, @Nonnull Direction facing, @Nonnull BlockPos pos, @Nonnull BlockState state)
    {
        if (!state.getBlock().equals(FDBlocks.blockCable.get()))
        {
            BlockEntity tileEntity = world.getBlockEntity(pos);
            return tileEntity != null && tileEntity.getCapability(CapabilityEnergy.ENERGY, facing).isPresent();
        }
        return true;
    }

    @Override
    @SuppressWarnings("deprecation")
    public void neighborChanged(@Nonnull BlockState state, @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Block fromBlock, @Nonnull BlockPos fromPos, boolean isMoving)
    {
        if (!world.isClientSide())
        {
            BlockEntity tileEntity = world.getBlockEntity(pos);
            if (tileEntity instanceof FDCableBlockEntity)
            {
                EnergyNetwork.Factory.get(world).enableBlock(pos, tileEntity::setChanged);
            }
        }
    }


    @Nullable
    @Override
    public BlockEntity newBlockEntity(@Nonnull BlockPos pos, @Nonnull BlockState state) {
        return new FDCableBlockEntity(pos, state);
    }

    @Nonnull
    @Override
    public List<ItemStack> getDrops(@Nonnull BlockState state, @Nonnull LootContext.Builder builder) {
        List<ItemStack> dropsOriginal = super.getDrops(state, builder);
        if (!dropsOriginal.isEmpty())
            return dropsOriginal;
        return Collections.singletonList(new ItemStack(this, 1));
    }
}
